/**
 * 
 */
package denaris.core;

import java.util.Enumeration;
import java.util.Vector;

import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.game.Sprite;

import denaris.core.controller.AbstractController;
import denaris.core.controller.AbstractControllerFeature;
import denaris.core.controller.AbstractReflectorControllerFeature;
import denaris.core.controller.ReflectorPosition;
import denaris.core.entity.AbstractEntity;
import denaris.core.listener.ICollisionListener;
import denaris.factory.ImageFactory;

/**
 * @author stkl23
 * 
 */
public class ReflectorEmitter extends AbstractParticleEmitter {
	private ICollisionListener collisionListener;
	private AbstractReflectorControllerFeature feature;
	private static Sprite collisionTester = new Sprite(ImageFactory.getImage("/denaris/resource/sprite/transparent.png"), 1, 1);
	
	/**
	 * The reflector weapon controller has probably the toughest task...
	 */
	public class ReflectorWeaponController extends AbstractController {
		/**
		 * Represents the path of the reflector weapon. The elements are
		 * ReflectorPosition elements
		 */
		private Vector path = new Vector(100);
		
		private AbstractControllerFeature originalFeature;
		
		private AbstractControllerFeature currentFeature;

		/** the number of frames that the weapon spreads */
		private int spreadDelay = 5;

		public ReflectorWeaponController(AbstractControllerFeature feature) {
			super();
			setOriginalFeature(feature);
			setCurrentFeature(feature);
		}

		public void reset() {
			spreadDelay = 5;
			path.removeAllElements();
			setCurrentFeature(getOriginalFeature());
		}

		public boolean advance() {
			currentFeature.advance();
			
			// should we remove the first element?
			if (spreadDelay == 0) {
				// yes
				path.removeElementAt(0);
			} else {
				spreadDelay--;
			}

			return path.size() > 0;
		}

		public Vector getPath() {
			return path;
		}

		public void setInitialPosition(int x, int y) {
			path.addElement(new ReflectorPosition(x, y));
		}

		/**
		 * we must override move since we have no sprite to move. Instead all
		 * line segments must be shifted
		 */
		public void move(int dx, int dy) {
			for (int i = 0; i < path.size(); i++) {
				ReflectorPosition next = (ReflectorPosition) path.elementAt(i);
				next.x += dx;
				next.y += dy;
			}
		}

		public AbstractControllerFeature getCurrentFeature() {
			return currentFeature;
		}

		public void setCurrentFeature(AbstractControllerFeature currentFeature) {
			this.currentFeature = currentFeature;
			setFeatureParent(currentFeature);
		}

		public AbstractControllerFeature getOriginalFeature() {
			return originalFeature;
		}

		public void setOriginalFeature(AbstractControllerFeature originalFeature) {
			this.originalFeature = originalFeature;
			setFeatureParent(originalFeature);
		}
		
		private void setFeatureParent(AbstractControllerFeature feature) {
			feature.setParent(this);
		}
	}

	/**
	 * A ReflectorBullet consists of a path of line segments. Whenever the
	 * leading path segments collides with the map it changes direction until it
	 * goes out of scope.
	 * 
	 */
	public class ReflectorBullet extends AbstractParticle {
		private Vector collisions = new Vector(10);

		public ReflectorBullet(AbstractController controller) {
			super(controller);
		}

		public ReflectorWeaponController getReflectorController() {
			return (ReflectorWeaponController) getController();
		}

		public void paint(Graphics g) {
			Vector path = getReflectorController().getPath();
			g.setColor(0xA0A0C0);
			g.setStrokeStyle(Graphics.SOLID);
			ReflectorPosition start = null;
			for (int i = 0; i < path.size(); i++) {
				ReflectorPosition next = (ReflectorPosition) path.elementAt(i);
				if (start != null) {
					g.drawLine(start.x, start.y, next.x, next.y);
				}
				start = next;
			}
		}

		public void update() {
			if (!controller.advance()) {
				notifyOutOfScope();
			}
		}

		public void setPosition(int x, int y) {
			// adjust the initial position a bit
			int dy = feature.getDeltaY() > 0 ? 12 : -4;
			((ReflectorWeaponController) controller).setInitialPosition(x + 25,
					y + dy);
		}

		/**
		 * returns a Vector of AbstractEntity objects that collide with this
		 * ReflectorBullet
		 */
		public Vector checkCollision(Vector entities) {
			collisions.removeAllElements();
			Vector path = getReflectorController().getPath();

			if (path.size() > 1) {
				ReflectorPosition head = (ReflectorPosition) path.lastElement();
				
				if (head != null) {
					collisionTester.setPosition(head.x, head.y);
					
					for (int j = 0; j < entities.size(); j++) {
						AbstractEntity entity = (AbstractEntity) entities.elementAt(j);
						Sprite s = entity.getController().getSprite();
						if (s.collidesWith(collisionTester, false)) {
							collisions.addElement(entity);
						}
					}
				}
			}
			return collisions;
		}
	}

	
	public ReflectorEmitter(AbstractReflectorControllerFeature feature) {
		super(1, AbstractReflectorControllerFeature.REFLECTOR_SPEED, AbstractReflectorControllerFeature.REFLECTOR_SPEED);
		this.feature = feature;
	}

	protected AbstractParticle createNew() {
		return new ReflectorBullet(new ReflectorWeaponController(feature));
	}

	public void checkCollision(Vector entities) {
		Enumeration e = elements();
		if (e.hasMoreElements()) {
			ReflectorBullet bullet = (ReflectorBullet) e.nextElement();
			Vector collisions = bullet.checkCollision(entities);
			// did we collide with anything?
			if (collisions.size() > 0) {
				// yes - notify the listener
				for (int i = 0; i < collisions.size(); i++) {
					notifyCollision(bullet, (AbstractEntity) collisions
							.elementAt(i));
				}
			}
		}
	}

	public void checkCollision(TiledMap map) {
		Enumeration e = elements();
		while (e.hasMoreElements()) {
			ReflectorBullet reflector = (ReflectorBullet) e.nextElement();
			((ReflectorWeaponController)reflector.getController()).getCurrentFeature().checkCollision(map);
		}
	}
	
	private void notifyCollision(ReflectorBullet source, AbstractEntity opponent) {
		if (collisionListener != null) {
			collisionListener.onCollision(source, opponent);
		}
	}

	public void setCollisionListener(ICollisionListener collisionListener) {
		this.collisionListener = collisionListener;
	}
}